package com.rc.evcharger.netty.server;

import com.alibaba.fastjson.JSON;
import com.rc.evcharger.common.code.StdControlCode;
import com.rc.evcharger.common.config.MqCommandConfig;
import com.rc.evcharger.common.config.RabbitListenerConstant;
import com.rc.evcharger.common.constant.SocketConstant;
import com.rc.evcharger.common.mq.MqProducer;
import com.rc.evcharger.common.utils.NumberUtil;
import com.rc.evcharger.common.utils.StringUtil;
import com.rc.evcharger.action.StdCommAction;
import com.rc.evcharger.dto.msg.TerminalMessage;
import com.rc.evcharger.dto.utils.MessageUtil;
import com.rc.evcharger.netty.listener.NettyListener;
import com.rc.evcharger.utils.DataDealExp;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Method;

import static com.rc.evcharger.dto.utils.MessageUtil.SPLIT;

/**
 * netty消息处理器，处理各种指令
 */
public class MessageHandler {
    private static Logger logger = LoggerFactory.getLogger(MessageHandler.class);

    private StdCommAction communicAction = null;
    private DataDealExp dataDealExp = null;
    private MqProducer mqProducer = null;
    private MqCommandConfig mqCommandConfig;

    /**
     * 从application中加载ReadAction对象
     */
    public MessageHandler() {
        ApplicationContext ac = NettyListener.getApplicationContext();
        mqProducer = (MqProducer) ac.getBean("mqProducer");
        communicAction = (StdCommAction) ac.getBean("stdCommAction");
        dataDealExp = (DataDealExp) ac.getBean("dataDealExp");
        mqCommandConfig = (MqCommandConfig) ac.getBean("mqCommandConfig");
    }

    /**
     * 公用处理请求
     *
     * @param message 端口出站
     * @return
     */
    public String doAction(TerminalMessage message, String channelId) {

        //不打印心跳和故障
        //if (!message.getContral().equals(ControlCode.HEARTBEAT_MONITOR) &&
        //        !message.getContral().equals(ControlCode.EXCEPTON_WARN)) {
        //    logger.warn("收到的指令:{}", message.getBody());
        //}
        logger.warn("收到的指令:{}", message.getBody());

        String response = "";
        try {
            NettyChannelHandler nettyChannelHandler = NettyChannelHandler.newInstance();
            if (nettyChannelHandler.getChannel(channelId) == null) { //如果连接为空不返回信息给桩，让桩重新登录
                return "";
            }

            NettyPileHandler nettyPileHandler = NettyPileHandler.newInstance();
            String dataJson = JSON.toJSONString(message);

            // 登录成功将终端逻辑地址放进map中，所有逻辑分本地执行和经MQ异步执行
            switch (message.getContral()) {
                case StdControlCode.LOGIN: //登录
                    //本地执行
                    response = communicAction.login(message);

                    if (response.length() > 0) {
                        logger.warn("登录的channelId：{},通道数量:{}", channelId, nettyChannelHandler.getChannels().size());
                        String channelId0 = nettyPileHandler.get(message.getRtua());
                        if (channelId0 != null && !channelId.equals(channelId0)) {//如果存在旧连接，就要先关闭
                            if (nettyChannelHandler.getChannel(channelId0) != null) {
                                nettyChannelHandler.getChannel(channelId0).close();
                            }
                        }
                        nettyPileHandler.add(message.getRtua(), channelId);

                        logger.warn("通道ID与逻辑地址" + message.getRtua() + ": id:" + nettyPileHandler.get(message.getRtua()));

                        //发送MQ消息由外部模块处理
                        //mqProducer.send(MqConfig.CONTROLL_A1, dataJson);
                        mqProducer.send(mqCommandConfig.getCONTROLL_A1(), dataJson);
                    }

                    break;
                case StdControlCode.ELEC_DATA_BALANCE://充电数据结算
                    //本地执行
                    response = communicAction.chargeBalance(message);

                    //发送MQ消息由外部模块处理
                    //mqProducer.send(MqConfig.CONTROLL_A6, dataJson);
                    //mqProducer.send(mqCommandConfig.getCONTROLL_A6(), dataJson);
                    mqProducer.sendDelay(mqCommandConfig.getCONTROLL_A6(), dataJson, 1000);

                    break;
                case StdControlCode.HEARTBEAT_MONITOR://心跳检验

                    //StringBuffer stringBuffer = new StringBuffer();
                    //Map<String, String> rtuaMap = nettyPileHandler.getRtuaMap();
                    //for (String s : rtuaMap.keySet()) {
                    //    stringBuffer.append(s).append(",");
                    //}
                    //logger.info("已存在的内存的通道key:{}", stringBuffer.toString());

                    //防止发送指令失败
                    String channelId0 = nettyPileHandler.get(message.getRtua());
                    if (channelId0 == null) {
                        nettyPileHandler.add(message.getRtua(), channelId);
                    }
                    //本地执行
                     response = communicAction.hearBeat(message);
                    //logger.info("心跳mq被调用前ruta:{},message:{},datajson:{}", message.getRtua(), message, dataJson);
                    //发送MQ消息由外部模块处理
                    mqProducer.send(mqCommandConfig.getCONTROLL_A4(), dataJson);
                    //mqProducer.sendLocalIpAdress(mqCommandConfig.getCONTROLL_A4(), dataJson);
                    break;
                case StdControlCode.ELEC_DATA_REPORT://充电数据上报
                    //本地执行
                    response = communicAction.chargeReport(message);

                    //延迟一秒，防止与0E指令重复插入订单
                    mqProducer.send(mqCommandConfig.getCONTROLL_A5(), dataJson);
                    //mqProducer.sendDelay(mqCommandConfig.getCONTROLL_A5(), dataJson, 1000);

                    break;
                case StdControlCode.RESP_CONTROL_CHARGE_START_STOP://启动和停止是同一指令，需要在MQ监听器进行区别
                    //本地执行
                    response = communicAction.readElecControl(message);

                    //发送MQ消息由外部模块处理
                    //mqProducer.send(MqConfig.CONTROLL_8E, dataJson);
                    mqProducer.send(mqCommandConfig.getCONTROLL_8E(), dataJson);

                    break;
                case StdControlCode.VICHELE_VIN_REPORT:        //VIN码指令
                    //本地执行
                    response = communicAction.readVinControl(message);
                    //发送MQ消息由外部模块处理
                    //mqProducer.send(MqConfig.CONTROLL_79, dataJson);
                    mqProducer.sendDelay(mqCommandConfig.getCONTROLL_79(), dataJson, 1200);

                    break;
                case StdControlCode.EXCEPTON_WARN:         //异常告警指令
                    //本地执行
                    response = communicAction.elecPileFault(message);
                    //发送MQ消息由外部模块处理
                    //mqProducer.send(MqConfig.CONTROLL_89, dataJson);
                    mqProducer.send(mqCommandConfig.getCONTROLL_89(), dataJson);

                    break;
                case StdControlCode.RESP_CONTROL_UNLOCK:       //解锁指令
                    //本地执行
                    response = communicAction.readUnLock(message);
                    //发送MQ消息由外部模块处理
                    //mqProducer.send(MqConfig.CONTROLL_8C, dataJson);
                    mqProducer.send(mqCommandConfig.getCONTROLL_8C(), dataJson);

                    break;
                case StdControlCode.CARD_NO_A3_REQUEST:       //刷卡
                    //本地执行
                    response = communicAction.readCardNo(message);
                    //发送MQ消息由外部模块处理
                    //mqProducer.send(MqConfig.CONTROLL_1C, dataJson);
                    mqProducer.send(mqCommandConfig.getCONTROLL_1C(), dataJson);

                    break;
                case StdControlCode.RESP_WRITE_OBJ_PARAM_REALTIME:       //校时回复指令
                    //本地执行
                    response = communicAction.timing(message);
                    //发送MQ消息由外部模块处理
//                    mqProducer.send(MqConfig.CONTROLL_8C, dataJson);

                    break;
                case StdControlCode.USER_ACCOUNT_34_RSPONSE:       //校时回复指令
                    //本地执行
                    response = communicAction.userAccount(message);
                    //发送MQ消息由外部模块处理
//                    mqProducer.send(MqConfig.CONTROLL_8C, dataJson);

                    break;
                case StdControlCode.ELEC_PILE_POWER_RESPONSE:       //查询功率指令
                    //本地执行
                    response = "";
                    //发送MQ消息由外部模块处理
                    mqProducer.send(RabbitListenerConstant.DOMAIN1_CONTROLL_C5_QUEUE, dataJson);

                    break;
                case StdControlCode.ELEC_PILE_LIMIT_POWER_RESPONSE:       //限制功能指令
                    //本地执行
                    response = "";
                    //发送MQ消息由外部模块处理
                    mqProducer.send(RabbitListenerConstant.DOMAIN1_CONTROLL_C6_QUEUE, dataJson);

                    break;
                default:
            }

            //不打印心跳和故障
            //if (!message.getContral().equals(ControlCode.RESP_HEARTBEAT_MONITOR) &&
            //        !message.getContral().equals(ControlCode.RESP_EXCEPTON_WARN)) {
            //    logger.warn("回复的指令：" + response + " 回复指令码:{}", message.getContral());
            //}
            logger.warn("回复的指令：" + response + " 回复指令码:{}", message.getContral());
        } catch (Exception e) {
            logger.error(e.getMessage());
            e.printStackTrace();
        }

        return response;
    }

    /**
     * 没有心跳时桩离线
     *
     * @param channelId
     */
    public void logoutWithoutHearbeat(String channelId) {
        NettyPileHandler nettyPileHandler = NettyPileHandler.newInstance();
        String rtua = nettyPileHandler.getRtuaByChannel(channelId);
        TerminalMessage message = new TerminalMessage();
        message.setRtua(rtua);
        communicAction.logout(message);   //删除定时任务
        //String dataJson = JSON.toJSONString(message);
        //mqProducer.send(mqCommandConfig.getCONTROLL_A2(), dataJson);
        logger.warn("电桩" + rtua + "登出");
        nettyPileHandler.remove(rtua);
    }

    /**
     * 发送指令接口
     *
     * @param msg
     */
    public static synchronized boolean write(TerminalMessage msg) {
        NettyPileHandler nettyPileHandler = NettyPileHandler.newInstance();
        //todo 兼容8位桩编号 拼接为12位
        if (msg.getRtua().length() == 11) {
            String turnRtua = StringUtil.jzTurnRtua(msg.getRtua(), null);
            msg.setRtua(SocketConstant.JZ_00_00 + SPLIT + turnRtua);
        }
        //Map<String, String> rtuaMap = nettyPileHandler.getRtuaMap();
        //System.out.println(rtuaMap.getClass());
        String channelId = nettyPileHandler.get(msg.getRtua());

        //打印通道ID
        //Map<String, String> rtuaMap = nettyPileHandler.getRtuaMap();
        //StringBuffer stringBuffer = new StringBuffer();
        //for (String s : rtuaMap.keySet()) {
        //    stringBuffer.append(s).append(",");
        //}
        //logger.info("存储的通道ruta有:{}", stringBuffer.toString());

        if (channelId == null) {
            logger.warn("发送指令时连接已断开,rtua:" + msg.getRtua());
            //删除失联的通道ID
            nettyPileHandler.remove(msg.getRtua());
            return false;
        }

        NettyChannelHandler nettyChannelHandler = NettyChannelHandler.newInstance();
        logger.warn("channelId：" + channelId);
        Channel channel = nettyChannelHandler.getChannel(channelId);
        String req = MessageUtil.createFrame(msg);
        //CustomerLog.loggerCommand("发送的指令："+req);
        logger.warn("发送的指令：" + req);
        byte[] respTmp = NumberUtil.hexStr2BinArr(req.replace(" ", ""));
        if (respTmp == null) {
            logger.warn("发送的指令为空");
        }
        ByteBuf resp = Unpooled.copiedBuffer(respTmp);
        if (channel == null) {
            logger.warn("channel为空");
        }
        ChannelFuture future = channel.writeAndFlush(resp);
        //同一个Channel最少间隔1s后才会发指令
        //future.addListener(new ChannelFutureListener() {
        //    @Override
        //    public void operationComplete(ChannelFuture future) throws Exception {
        //        Thread.sleep(1000);
        //    }
        //});
        return true;
    }

    public String dealMsg(ChannelHandlerContext ctx, String body) {
        Object object = dataDealExp;
        Method[] methods = DataDealExp.class.getDeclaredMethods();
        String response = null;
        for (Method method : methods) {
            if (method.getName().equals("dealMsg")) {
                try {
                    response = (String) method.invoke(object, ctx, body);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }
        return response;
    }
}
